﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UFIDA.U9.Base.Doc;
using UFIDA.U9.CBO.Pub.Controller;
using UFIDA.U9.CBO.SCM.Item;
using UFIDA.U9.CBO.SCM.Node;
using UFSoft.UBF.Business;
using UFSoft.UBF.Business.Tool;
using UFSoft.UBF.Transactions;
using UFSoft.UBF.Util.Data;

namespace ufida.u9.ait.openapi.utils
{
    internal class CodeUtils
    {
        public static ResultDTOData EntitySave<T, U>(
            object bpObj,
            List<T> dtoData,
            Func<T, U> CreateEntity,
            Func<T, U, U> UpdateEntity,
            bool useTrans = false
            ) where U : BusinessEntity
        {
            /*
            启用UBF事务,在UBF中设置support
            这样可以在extend代码中返回错误信息,而不是框架报错
            一般采用下述using模式,考虑通过参数判断,采用代码模式显示创建和释放
            using (UBFTransactionScope scope = new UBFTransactionScope(TransactionOption.RequiresNew))
            {}

            如果在子方法中重复启用事务TransactionOption.RequiresNew,服务会锁死,切记检查

            */
            UBFTransactionScope scope = null;
            try
            {
                //启用UBF事务
                if (useTrans) scope = new UBFTransactionScope(TransactionOption.RequiresNew);

                if (dtoData == null || dtoData.Count == 0)
                    throw new Exception("传入参数为空");
                List<CommonArchiveDataDTOData> retlist = new List<CommonArchiveDataDTOData>();

                foreach (var dto in dtoData)
                {
                    //查询实体
                    String path = DTOUtils.GetDocNoPath(dto);
                    EntityQuery query = new EntityQuery(typeof(U).FullName);
                    System.Collections.IList list = query.FindAll(path);

                    U item = list?.Count > 0 ? (U)list[0]:null;
                    if (item == null)
                    {
                        //新增逻辑
                        if(CreateEntity != null)
                        {
                            item = CreateEntity(dto);
                        }
                        else
                        {
                            Type type = typeof(U);
                            throw new Exception($"查找实体失败!实体名称:[{ExtendHelpAPI.GetEntityResource(type.FullName)}],实体编码[{DTOUtils.GetDocNo(dto)}]");
                        }
                    }
                    else
                    {
                        //修改逻辑
                        item = UpdateEntity(dto,item);
                    }
                    //返回值
                    CommonArchiveDataDTOData ret = new CommonArchiveDataDTOData()
                    {
                        ID = item.ID,
                        Code = EntityUtils.GetEntityCode(item),
                        Name = EntityUtils.GetEntityName(item),
                    };
                    retlist.Add(ret);
                }

                //整体事务提交
                if (scope != null) scope.Commit();
                return ResultUtils.success(bpObj, dtoData, retlist);
            }
            catch (Exception ex)
            {
                //事务回滚
                if (scope != null) scope.Rollback();
                return ResultUtils.error(bpObj, dtoData, ex);
            }
            finally
            {
                //查看源码 Release()和Dispose()作用相同
                if (scope != null) scope.Release();
            }
        }
        /// <summary>
        /// 单据保存
        /// 不需要事务
        /// </summary>
        /// <typeparam name="T">传入DTOData泛型</typeparam>
        /// <typeparam name="U">单据BE泛型,基类=DOC</typeparam>
        /// <param name="bpObj">BP</param>
        /// <param name="dtoData">DTO集合</param>
        /// <param name="CreateDoc">单据创建函数,不能为空,传入dtodata,返回BE</param>
        /// <param name="ApproveDoc">单据审核函数,为空时不审核,传入BE,返回BE</param>
        /// <param name="DeleteDoc">单据删除函数,接口报错时,手工删除单据</param>
        /// <returns></returns>
        public static ResultDTOData DocSaveNT<T, U>(
            object bpObj,
            List<T> dtoData,
            Func<T, U> CreateDoc,
            Func<U, U> ApproveDoc,
            Func<U, U> DeleteDoc = null
            ) where U : Doc
        {
            return DocSaveTemplate<T, U>(bpObj, dtoData, CreateDoc, ApproveDoc, DeleteDoc, false);
        }
        /// <summary>
        /// 单据实体保存
        /// 需要事务
        /// </summary>
        /// <typeparam name="T">传入DTOData泛型</typeparam>
        /// <typeparam name="U">单据BE泛型,基类=DOC</typeparam>
        /// <param name="bpObj">BP</param>
        /// <param name="dtoData">DTO集合</param>
        /// <param name="CreateDoc">单据创建函数,不能为空,传入dtodata,返回BE</param>
        /// <param name="ApproveDoc">单据审核函数,为空时不审核,传入BE,返回BE</param>
        /// <returns></returns>
        public static ResultDTOData DocSaveRT<T, U>(
            object bpObj,
            List<T> dtoData,
            Func<T, U> CreateDoc,
            Func<U, U> ApproveDoc
            ) where U : Doc
        {
            return DocSaveTemplate<T, U>(bpObj, dtoData, CreateDoc, ApproveDoc, null, true);
        }

        /// <summary>
        /// 单据保存代码模板
        /// </summary>
        /// <typeparam name="T">传入DTOData泛型</typeparam>
        /// <typeparam name="U">单据BE泛型,基类=DOC</typeparam>
        /// <param name="bpObj">BP</param>
        /// <param name="dtoData">DTO集合</param>
        /// <param name="CreateDoc">单据创建函数,不能为空,传入dtodata,返回BE</param>
        /// <param name="ApproveDoc">单据审核函数,为空时不审核,传入BE,返回BE</param>
        /// <param name="DeleteDoc">单据删除函数,接口报错时,手工删除单据</param>
        /// <param name="useTrans">是否启用事务,默认=true</param>
        /// <returns>单据DocDTO集合</returns>
        private static ResultDTOData DocSaveTemplate<T, U>(
            object bpObj,
            List<T> dtoData,
            Func<T, U> CreateDoc,
            Func<U, U> ApproveDoc,
            Func<U,U> DeleteDoc=null,
            bool useTrans = true
            ) where U : Doc
        {
            /*
            启用UBF事务,在UBF中设置support
            这样可以在extend代码中返回错误信息,而不是框架报错
            一般采用下述using模式,考虑通过参数判断,采用代码模式显示创建和释放
            using (UBFTransactionScope scope = new UBFTransactionScope(TransactionOption.RequiresNew))
            {}

            如果在子方法中重复启用事务TransactionOption.RequiresNew,服务会锁死,切记检查

            */
            UBFTransactionScope scope = null;
            U doc = null;
            try
            {
                //启用UBF事务
                if (useTrans) scope = new UBFTransactionScope(TransactionOption.RequiresNew);

                if (dtoData == null || dtoData.Count == 0)
                    throw new Exception("传入参数为空");
                List<DocDTOData> retlist = new List<DocDTOData>();

                foreach (var dto in dtoData)
                {
                    //查询单据实体
                    String path = DTOUtils.GetDocNoPath(dto);
                    EntityQuery query = new EntityQuery(typeof(U).FullName);
                    System.Collections.IList list = query.FindAll(path);

                    if (list == null || list.Count <= 0)
                    {
                        //新增逻辑
                        doc = CreateDoc(dto);
                        //修改单号
                        EntityUtils.UpdateDocNo<U>(doc.ID, ReflexUtils.GetValue("DocNo", dto));
                        //审核
                        if (ApproveDoc != null)
                        {
                            doc = ApproveDoc(doc);
                        }
                        //返回值
                        U9Utils.ClearCache();
                        doc = (U)doc.Key.GetEntity();
                        DocDTOData ret = new DocDTOData()
                        {
                            ID = doc.ID.ToString(),
                            DocNo = doc.DocNo,
                        };
                        retlist.Add(ret);
                    }
                    else
                    {
                        //修改逻辑
                        throw new AITException(599, $"单号:{DTOUtils.GetDocNo(dto)}已经存在,不能生成!");
                    }
                }

                //整体事务提交
                if (scope != null) scope.Commit();
                return ResultUtils.success(bpObj, dtoData, retlist);
            }
            catch (Exception ex)
            {
                //事务回滚
                if (scope != null) scope.Rollback();
                //手工删除单据
                if (DeleteDoc != null)
                {
                    try
                    {
                        DeleteDoc(doc);
                    }catch(Exception ex1)
                    {
                        //日志记录删除错误信息
                        LogUtils.Log(ex1.Message);
                        LogUtils.Log(ex1.StackTrace);
                        //返回真实异常信息
                        return ResultUtils.error(bpObj, dtoData, ex);
                    }
                }
                return ResultUtils.error(bpObj, dtoData, ex);
            }
            finally
            {
                //查看源码 Release()和Dispose()作用相同
                if (scope != null) scope.Release();
            }
        }

        public static ResultDTOData DocUpdateNT<T, U>(
            object bpObj,
            List<T> dtoData,
            Func<U, U> UpdateDoc
            ) where U : Doc
        {
            return DocUpdateTemplate<T, U>(bpObj, dtoData, UpdateDoc, false);
        }

        public static ResultDTOData DocUpdateRT<T, U>(
            object bpObj,
            List<T> dtoData,
            Func<U, U> UpdateDoc
            ) where U : Doc
        {
            return DocUpdateTemplate<T, U>(bpObj, dtoData, UpdateDoc, true);
        }

        /// <summary>
        /// 单据修改代码模板
        /// 修改时支持传入BE实体
        /// 常用于单据审批
        /// </summary>
        /// <typeparam name="T">传入DTOData泛型</typeparam>
        /// <typeparam name="U">单据BE泛型,基类=DOC</typeparam>
        /// <param name="bpObj">BP</param>
        /// <param name="dtoData">DTO集合</param>
        /// <param name="UpdateDoc">单据修改/审核函数,必传,传入BE,返回BE</param>
        /// <param name="useTrans">是否启用事务,默认=false</param>
        /// <returns>单据DocDTO集合</returns>
        private static ResultDTOData DocUpdateTemplate<T, U>(
        object bpObj,
        List<T> dtoData,
        Func<U, U> UpdateDoc,
        bool useTrans = false
        ) where U : Doc
        {
            /*
             * 这里取消了事务,是因为在 费用报销 模块中的报销单据,提交BP调用时,如果启用事务,BP会卡死
             * 后续考虑将启用事务和不启用事务 通过参数传入
             */

            //启用UBF事务,在UBF中设置support
            //这样可以在extend代码中返回错误信息,而不是框架报错
            //一般采用下述using模式,考虑通过参数判断,采用代码模式显示创建和释放
            //using (UBFTransactionScope scope = new UBFTransactionScope(TransactionOption.RequiresNew))
            //{}
            UBFTransactionScope scope = null;
            try
            {
                //启用UBF事务
                if (useTrans) scope = new UBFTransactionScope(TransactionOption.RequiresNew);

                if (dtoData == null || dtoData.Count == 0)
                    throw new Exception("传入参数为空");
                List<DocDTOData> retlist = new List<DocDTOData>();

                foreach (var dto in dtoData)
                {
                    //查询单据实体
                    String path = DTOUtils.GetDocNoPath(dto,"DocNo");
                    EntityQuery query = new EntityQuery(typeof(U).FullName);
                    System.Collections.IList list = query.FindAll(path);

                    if (list == null || list.Count <= 0)
                    {
                        //新增逻辑
                        throw new AITException(599, $"单号:{DTOUtils.GetDocNo(dto)}不存在!");
                    }
                    else
                    {
                        //修改逻辑
                        U doc = (U)list[0];
                        UpdateDoc(doc);
                        DocDTOData retdto = new DocDTOData()
                        {
                            DocNo = doc.DocNo,
                            ID = doc.ID.ToString(),
                        };

                        retlist.Add(retdto);
                    }
                }

                //整体事务提交
                if (scope != null) scope.Commit();
                return ResultUtils.success(bpObj, dtoData, retlist);
            }
            catch (Exception ex)
            {
                //事务回滚
                if (scope != null) scope.Rollback();
                return ResultUtils.error(bpObj, dtoData, ex);
            }
            finally
            {
                //查看源码 Release()和Dispose()作用相同
                if (scope != null) scope.Release();
            }
        }

        /// <summary>
        /// 单据修改代码模板
        /// 修改时支持传入DTO,BE实体
        /// 常用于单据修改/变更
        /// </summary>
        /// <typeparam name="T">传入DTOData泛型</typeparam>
        /// <typeparam name="U">单据BE泛型,基类=DOC</typeparam>
        /// <param name="bpObj">BP</param>
        /// <param name="dtoData">DTO集合</param>
        /// <param name="UpdateDoc">单据修改/审核函数,必传,传入DTO,BE,返回BE</param>
        /// <param name="useTrans">是否启用事务,默认=false</param>
        /// <returns>单据DocDTO集合</returns>
        public static ResultDTOData DocUpdateTemplate2<T, U>(
        object bpObj,
        List<T> dtoData,
        Func<T, U, U> UpdateDoc,
        bool useTrans = false
        ) where U : Doc
        {
            /*
             * 这里取消了事务,是因为在 费用报销 模块中的报销单据,提交BP调用时,如果启用事务,BP会卡死
             * 后续考虑将启用事务和不启用事务 通过参数传入
             */

            //启用UBF事务,在UBF中设置support
            //这样可以在extend代码中返回错误信息,而不是框架报错
            //一般采用下述using模式,考虑通过参数判断,采用代码模式显示创建和释放
            //using (UBFTransactionScope scope = new UBFTransactionScope(TransactionOption.RequiresNew))
            //{}
            UBFTransactionScope scope = null;
            try
            {
                //启用UBF事务
                if (useTrans) scope = new UBFTransactionScope(TransactionOption.RequiresNew);

                if (dtoData == null || dtoData.Count == 0)
                    throw new Exception("传入参数为空");
                List<DocDTOData> retlist = new List<DocDTOData>();

                foreach (var dto in dtoData)
                {
                    //查询单据实体
                    String path = DTOUtils.GetDocNoPath(dto, "DocNo");
                    EntityQuery query = new EntityQuery(typeof(U).FullName);
                    System.Collections.IList list = query.FindAll(path);

                    if (list == null || list.Count <= 0)
                    {
                        //新增逻辑
                        throw new AITException(599, $"单号:{DTOUtils.GetDocNo(dto)}不存在!");
                    }
                    else
                    {
                        //修改逻辑
                        U doc = (U)list[0];
                        UpdateDoc(dto, doc);
                        DocDTOData retdto = new DocDTOData()
                        {
                            DocNo = doc.DocNo,
                            ID = doc.ID.ToString(),
                        };

                        retlist.Add(retdto);
                    }
                }

                //整体事务提交
                if (scope != null) scope.Commit();
                return ResultUtils.success(bpObj, dtoData, retlist);
            }
            catch (Exception ex)
            {
                //事务回滚
                if (scope != null) scope.Rollback();
                return ResultUtils.error(bpObj, dtoData, ex);
            }
            finally
            {
                //查看源码 Release()和Dispose()作用相同
                if (scope != null) scope.Release();
            }
        }
    }
}
